/*
 * drivers/char/sunxi-scr/smartcard.c
 *
 * Copyright (C) 2016 Allwinner.
 * fuzhaoke <fuzhaoke@allwinnertech.com>
 *
 * Decode for ISO7816 smart card
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 */

#include <linux/module.h>
#include "smartcard.h"


void smartcard_ta1_decode(struct smc_atr_para *psmc_atr, uint8_t ta1)
{
	pr_debug("%s: enter!!\n", __func__);

	switch ((ta1>>4)&0xf) {
	case 0x0:
		psmc_atr->FMAX = 4;
		psmc_atr->F = 372;
		break;
	case 0x1:
		psmc_atr->FMAX = 5;
		psmc_atr->F = 372;
		break;
	case 0x2:
		psmc_atr->FMAX = 6;
		psmc_atr->F = 558;
		break;
	case 0x3:
		psmc_atr->FMAX = 8;
		psmc_atr->F = 744;
		break;
	case 0x4:
		psmc_atr->FMAX = 12;
		psmc_atr->F = 1116;
		break;
	case 0x5:
		psmc_atr->FMAX = 16;
		psmc_atr->F = 1488;
		break;
	case 0x6:
		psmc_atr->FMAX = 20;
		psmc_atr->F = 1860;
		break;
	case 0x9:
		psmc_atr->FMAX = 5;
		psmc_atr->F = 512;
		break;
	case 0xA:
		psmc_atr->FMAX = 7;
		psmc_atr->F = 768;
		break;
	case 0xB:
		psmc_atr->FMAX = 10;
		psmc_atr->F = 1024;
		break;
	case 0xC:
		psmc_atr->FMAX = 15;
		psmc_atr->F = 1536;
		break;
	case 0xD:
		psmc_atr->FMAX = 20;
		psmc_atr->F = 2048;
		break;
	default:  /* 0x7/0x8/0xE/0xF */
		psmc_atr->FMAX = 4;
		psmc_atr->F = 372;
		pr_err("Unsupport ta1 = 0x%x\n", ta1);
		break;
	}

	switch (ta1&0xf) {
	case 0x1:
		psmc_atr->D = 1;
		break;
	case 0x2:
		psmc_atr->D = 2;
		break;
	case 0x3:
		psmc_atr->D = 4;
		break;
	case 0x4:
		psmc_atr->D = 8;
		break;
	case 0x5:
		psmc_atr->D = 16;
		break;
	case 0x6:
		psmc_atr->D = 32;
		break;
	case 0x8:
		psmc_atr->D = 12;
		break;
	case 0x9:
		psmc_atr->D = 20;
		break;
	default: /* 0x0/0x7/0xA/0xB/0xC/0xD/0xE/0xF */
		psmc_atr->D = 1;
		pr_err("Unsupport ta1 = 0x%x\n", ta1);
		break;
	}

	return ;
}

void smartcard_tb1_decode(struct smc_atr_para *psmc_atr, uint8_t tb1)
{
	pr_debug("%s: enter!!\n", __func__);

	switch ((tb1>>5)&0x3) {
	case 0:
		psmc_atr->I = 25;
		break;
	case 1:
		psmc_atr->I = 50;
		break;
	case 2:
		psmc_atr->I = 100;
		break;
	default:
		psmc_atr->I = 50;
	}

	if (((tb1&0x1f) > 4) && ((tb1&0x1f) < 26)) {
		psmc_atr->P = (tb1&0x1f); /* 5~25 in Volts */
	} else if (0 == (tb1&0x1f)) {
		psmc_atr->P = 0;
	} else {
		psmc_atr->P = 5;
	}

	return ;
}

/* ATR data format: max 33 byte(include TS)
 * [TS|T0|TA1|TB1|TC1|TD1|TA2|TB2|TC2|TD2|...|T1|T2|........|TK|TCK]
 *       |-------- max 16 byte --------------|-- max 15 byte --|verify
 * |--------------------- max 33 byte -------------------------|
 * TS: Initial character
 *	0x3b:direct convention, 0x3f:inverse convention
 * T0: Format character
 *	[bit7|bit6|bit5|bit4|bit3|bit2|bit1|bit0]
 *	|------- Y1 --------|-------- K --------|
 *	Y1: indicator for the presence of the interface character
 *		TA1 is transmitted when bit[4] = 1
 *		TB1 is transmitted when bit[5] = 1
 *		TC1 is transmitted when bit[6] = 1
 *		TD1 is transmitted when bit[7] = 1
 *	K: history number
 * TAi: Interface character
 *	code FI,DI
 * TBi: Interface character
 *	code II,PI
 * TCi: Interface character
 *	code N
 * TDi: Interface charater
 *	code Yi+1, T
 *	[bit7|bit6|bit5|bit4|bit3|bit2|bit1|bit0]
 *	|------ Yi + 1 -----|-------- T --------|
 *	Yi + 1: indicator for the presence of the interface character
 *		TAi+1 is transmitted when bit[4] = 1
 *		TBi+1 is transmitted when bit[5] = 1
 *		TCi+1 is transmitted when bit[6] = 1
 *		TDi+1 is transmitted when bit[7] = 1
 *	T: Protocol type for subsequent transmission
 *		T=0: character transmit
 *		T=1: block transmit
 *		T=other: reserved
 * T1~TK: history byte, infomation of the card like manufacture name
 * TCK: verify character, make sure the data is correctly
*/
void smartcard_atr_decode(struct smc_atr_para *psmc_atr, struct smc_pps_para *psmc_pps,
			  uint8_t *pdata, uint8_t with_ts)
{
	uint8_t index = 0;
	uint8_t temp;
	uint8_t i;

	pr_debug("%s: Enter...\n", __func__);

	psmc_pps->ppss = 0xff;
	psmc_pps->pps0 = 0;

	if (with_ts) {
		psmc_atr->TS = pdata[0]; /* TS */
		index++;
	}
	temp = pdata[index]; /* T0 */
	index++;
	psmc_atr->TK_NUM = temp & 0xf;

	/* TA1 */
	if (temp & 0x10) {
		smartcard_ta1_decode(psmc_atr, pdata[index]);
		psmc_pps->pps0 |= 0x1<<4;
		psmc_pps->pps1 = pdata[index];
		index++;
	}

	/* TB1 */
	if (temp & 0x20) {
		smartcard_tb1_decode(psmc_atr, pdata[index]);
		index++;
	}

	/* TC1 */
	if (temp & 0x40) {
		psmc_atr->N = pdata[index] & 0xff;
		index++;
	}

	/* TD1 */
	if (temp & 0x80) {
		pr_debug("%s: TD1 parse 0x%x !!\n", __func__, pdata[index]);
		temp = pdata[index];
		psmc_atr->T = temp & 0xf;
		psmc_pps->pps0 |= temp & 0xf;

		/* Adjust Guard Time */
		if (psmc_atr->N == 0xff) {
			if (psmc_atr->T == 1)
				psmc_atr->N = 1;
			else
				psmc_atr->N = 2;
		}
		index++;
	} else {
		if (psmc_atr->N == 0xff)
			psmc_atr->N = 2;
		goto rx_tk;
	}

	/* TA2 */
	if (temp & 0x10) {
		pr_debug("TA2 Exist!!\n");
		index++;
	}

	/* TB2 */
	if (temp & 0x20) {
		pr_debug("TB2 Exist!!\n");
		index++;
	}

	/* TC2 */
	if (temp & 0x40) {
		pr_debug("TC2 Exist!!\n");
		index++;
	}

	/* TD2 */
	if (temp & 0x80) {
		pr_debug("TD2 Exist!!\n");
		temp = pdata[index];
		index++;
	} else {
		goto rx_tk;
	}

	/* TA3 */
	if (temp & 0x10) {
		pr_debug("TA3 Exist!!\n");
		index++;
	}

	/* TB3 */
	if (temp & 0x20) {
		pr_debug("TB3 Exist!!\n");
		index++;
	}

	/* TC3 */
	if (temp & 0x40) {
		pr_debug("TC3 Exist!!\n");
		index++;
	}

	/* TD3 */
	if (temp & 0x80) {
		pr_debug("TD3 Exist!!\n");
		temp = pdata[index];
		index++;
	} else {
		goto rx_tk;
	}

	/* TA4 */
	if (temp & 0x10) {
		pr_debug("TA4 Exist!!\n");
		index++;
	}

	/* TB4 */
	if (temp & 0x20) {
		pr_debug("TB4 Exist!!\n");
		index++;
	}

	/* TC4 */
	if (temp & 0x40) {
		pr_debug("TC4 Exist!!\n");
		index++;
	}

	/* TD4 */
	if (temp & 0x80) {
		pr_debug("TD4 Exist!!\n");
		temp = pdata[index];
		index++;
	}

rx_tk:
	for (i = 0; i < (psmc_atr->TK_NUM); i++) {
		psmc_atr->TK[i] = pdata[index++];
	}

	psmc_pps->pck = psmc_pps->ppss;
	psmc_pps->pck ^= psmc_pps->pps0;
	if (psmc_pps->pps0&(0x1<<4)) {
		psmc_pps->pck ^= psmc_pps->pps1;
	}
	if (psmc_pps->pps0&(0x1<<5)) {
		psmc_pps->pck ^= psmc_pps->pps2;
	}
	if (psmc_pps->pps0&(0x1<<6)) {
		psmc_pps->pck ^= psmc_pps->pps3;
	}

	return ;
}



